package com.yp.infrastructure.axon.interceptor.command;

import com.yp.infrastructure.axon.constants.EnumErrorMessage;
import com.yp.infrastructure.axon.exception.AxonException;
import com.yp.infrastructure.axon.interceptor.BaseMessageHandlerInterceptor;
import com.yp.infrastructure.axon.interceptor.IAxonCommandHandlerInterceptor;
import com.yp.infrastructure.common.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.CommandMessage;
import org.axonframework.messaging.InterceptorChain;
import org.axonframework.messaging.MessageHandlerInterceptor;
import org.axonframework.messaging.unitofwork.UnitOfWork;

import javax.annotation.Nonnull;

/**
 * <b><font color="red">Message handler interceptors can take action both before and after command processing.</font></b>
 * Interceptors can even block command processing altogether, for example for security reasons.
 * <p>
 * Interceptors must implement the MessageHandlerInterceptor interface.
 * This interface declares one method, handle, that takes two parameters: the current UnitOfWork and an InterceptorChain.
 * The InterceptorChain is used to continue the dispatching process.
 * The UnitOfWork gives you (1) the message being handled and (2) provides the possibility to tie in logic prior,
 * during or after (command) message handling (see Unit Of Work for more information about the phases).
 * </p>
 *
 * <p>
 * Unlike dispatch interceptors,<font color="red"> handler interceptors are invoked in the context of the command handler.</font>
 * That means they can attach correlation data based on the message being handled to the unit of work, for example.
 * This correlation data will then be attached to messages being created in the context of that unit of work.
 * </p>
 *
 * <p>
 * Handler interceptors are also typically used to manage transactions around the handling of a command.
 * To do so, register a TransactionManagingInterceptor, which in turn is configured with a TransactionManager
 * to start and commit (or roll back) the actual transaction.
 * </p>
 *
 * <p>
 * 如果要对EventHandler进行拦截，将本类的泛型{@link CommandMessage}改为{@link org.axonframework.eventhandling.EventMessage}
 * </p>
 */

@Slf4j
public class CmdMessageHandlerInterceptor extends BaseMessageHandlerInterceptor implements MessageHandlerInterceptor<CommandMessage<?>> {


    private final IAxonCommandHandlerInterceptor interceptor;

    public CmdMessageHandlerInterceptor(@Nonnull IAxonCommandHandlerInterceptor interceptor) {
        this.interceptor = interceptor;
    }

    /**
     * <p>
     * 此处可以作访问权限验证，正常项目下，此功能不放在这里
     * examples:
     * </p>
     * <pre>
     * CommandMessage<?> command = unitOfWork.getMessage();
     * String userId = Optional.ofNullable(command.getMetaData().get("userId"))
     *                                 .map(uId -> (String) uId)
     *                                 .orElseThrow(IllegalCommandException::new);
     * if ("axonUser".equals(userId)) {
     *    return interceptorChain.proceed();
     * }
     * return null;
     * </pre>
     *
     * @param unitOfWork       unitOfWork
     * @param interceptorChain interceptorChain
     * @return .
     * @throws BaseException .
     */
    @Override
    public Object handle(@Nonnull UnitOfWork<? extends CommandMessage<?>> unitOfWork,
                       @Nonnull  InterceptorChain interceptorChain) throws BaseException {
        try {
            CommandMessage<?> message = unitOfWork.getMessage();
            interceptor.onCommandMessageHandlerInterceptor(message.getPayload());
            return interceptorChain.proceed();
        } catch (Throwable throwable) {
            if (throwable instanceof BaseException) {
                IllegalStateException e = (IllegalStateException) throwable;
                log.error(e.getMessage());
            }
            throw new AxonException(EnumErrorMessage.AXON_ERROR, throwable);
        }
    }
}
